package binding

import (
	"math"
	"mime/multipart"
	"reflect"
	"time"
	"unsafe"
)

var (
	typeDuration   = reflect.TypeOf(time.Duration(0))
	typeTime       = reflect.TypeOf(time.Time{})
	typeFileHeader = reflect.TypeOf(&multipart.FileHeader{})
)

func getType(v interface{}) reflect.Type {
	if v == nil {
		return nil
	}
	var rt reflect.Type
	switch t := v.(type) {
	case reflect.Type:
		rt = t
	case reflect.Value:
		rt = t.Type()
	default:
		rt = reflect.TypeOf(t)
	}

	if rt.Kind() == reflect.Ptr {
		rt = rt.Elem()
	}

	return rt
}

func newValue(rt reflect.Type) reflect.Value {
	rk := rt.Kind()

	if rk == reflect.Ptr {
		rt = rt.Elem()
	}
	v := reflect.New(rt)

	if rk == reflect.Ptr {
		return v
	}

	return v.Elem()
}

func b2s(data []byte) string {
	return *(*string)(unsafe.Pointer(&data))
}

func s2b(s string) (b []byte) {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	bh.Data = sh.Data
	bh.Len = sh.Len
	bh.Cap = sh.Len
	return b
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return math.Float64bits(v.Float()) == 0
	case reflect.Complex64, reflect.Complex128:
		c := v.Complex()
		return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
	case reflect.Array:
		for i := 0; i < v.Len(); i++ {
			if !v.Index(i).IsZero() {
				return false
			}
		}
		return true
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
		return v.IsNil()
	case reflect.String:
		return v.Len() == 0
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			if !v.Field(i).IsZero() {
				return false
			}
		}
		return true
	default:
		return false
	}
}
